<h1>路由设置</h1>

<p>什么是路由设置呢？前面介绍的 MVC 结构执行时，介绍过 beego 存在三种方式的路由:固定路由、正则路由、自动路由，接下来详细的讲解如何使用这三种路由。</p>

<h2>基础路由</h2>

<p>从 beego 1.2 版本开始支持了基本的 RESTful 函数式路由,应用中的大多数路由都会定义在 <code>routers/router.go</code> 文件中。最简单的 beego 路由由 URI 和闭包函数组成。</p>

<h3>基本 GET 路由</h3>

<pre><code>beego.Get(&quot;/&quot;,func(ctx *context.Context){
     ctx.Output.Body([]byte(&quot;hello world&quot;))
})
</code></pre>

<h3>基本 POST 路由</h3>

<pre><code>beego.Post(&quot;/alice&quot;,func(ctx *context.Context){
     ctx.Output.Body([]byte(&quot;bob&quot;))
})
</code></pre>

<h3>注册一个可以响应任何 HTTP 的路由</h3>

<pre><code>beego.Any(&quot;/foo&quot;,func(ctx *context.Context){
     ctx.Output.Body([]byte(&quot;bar&quot;))
})
</code></pre>

<h3>所有的支持的基础函数如下所示</h3>

<ul>
    <li>beego.Get(router, beego.FilterFunc)<br /></li>
    <li>beego.Post(router, beego.FilterFunc)<br /></li>
    <li>beego.Put(router, beego.FilterFunc)<br /></li>
    <li>beego.Patch(router, beego.FilterFunc)<br /></li>
    <li>beego.Head(router, beego.FilterFunc)<br /></li>
    <li>beego.Options(router, beego.FilterFunc)<br /></li>
    <li>beego.Delete(router, beego.FilterFunc)<br /></li>
    <li>beego.Any(router, beego.FilterFunc)<br /></li>
</ul>

<h3>支持自定义的 handler 实现</h3>

<p>有些时候我们已经实现了一些 rpc 的应用,但是想要集成到 beego 中,或者其他的 httpserver 应用,集成到 beego 中来.现在可以很方便的集成:</p>

<pre><code>s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), &quot;application/json&quot;)
s.RegisterService(new(HelloService), &quot;&quot;)
beego.Handler(&quot;/rpc&quot;, s)
</code></pre>

<p><code>beego.Handler(router, http.Handler)</code> 这个函数是关键,第一个参数表示路由 URI, 第二个就是你自己实现的 <code>http.Handler</code>, 注册之后就会把所有 rpc 作为前缀的请求分发到 <code>http.Handler</code> 中进行处理.</p>

<p>这个函数其实还有第三个参数就是是否是前缀匹配,默认是 false, 如果设置了 true, 那么就会在路由匹配的时候前缀匹配,即 <code>/rpc/user</code> 这样的也会匹配去运行</p>

<h3>路由参数</h3>

<p>后面会讲到固定路由,正则路由,这些参数一样适用于上面的这些函数</p>

<h2>RESTful Controller 路由</h2>

<p>在介绍这三种 beego 的路由实现之前先介绍 RESTful，我们知道 RESTful 是一种目前 API 开发中广泛采用的形式，beego 默认就是支持这样的请求方法，也就是用户 Get 请求就执行 Get 方法，Post 请求就执行 Post 方法。因此默认的路由是这样 RESTful 的请求方式。</p>

<h2>固定路由</h2>

<p>固定路由也就是全匹配的路由，如下所示：</p>

<pre><code>beego.Router(&quot;/&quot;, &amp;controllers.MainController{})
beego.Router(&quot;/admin&quot;, &amp;admin.UserController{})
beego.Router(&quot;/admin/index&quot;, &amp;admin.ArticleController{})
beego.Router(&quot;/admin/addpkg&quot;, &amp;admin.AddController{})
</code></pre>

<p>如上所示的路由就是我们最常用的路由方式，一个固定的路由，一个控制器，然后根据用户请求方法不同请求控制器中对应的方法，典型的 RESTful 方式。</p>

<h2>正则路由</h2>

<p>为了用户更加方便的路由设置，beego 参考了 sinatra 的路由实现，支持多种方式的路由：</p>

<ul>
    <li><p>beego.Router(&ldquo;/api/?:id&rdquo;, &amp;controllers.RController{})</p>

        <p>默认匹配   //例如对于URL&rdquo;/api/123&rdquo;可以匹配成功，此时变量&rdquo;:id&rdquo;值为&rdquo;123&rdquo;</p></li>

    <li><p>beego.Router(&ldquo;/api/:id&rdquo;, &amp;controllers.RController{})</p>

        <p>默认匹配   //例如对于URL&rdquo;/api/123&rdquo;可以匹配成功，此时变量&rdquo;:id&rdquo;值为&rdquo;123&rdquo;，但URL&rdquo;/api/&ldquo;匹配失败</p></li>

    <li><p>beego.Router(&ldquo;/api/:id([0-9]+)&ldquo;, &amp;controllers.RController{})</p>

        <p>自定义正则匹配 //例如对于URL&rdquo;/api/123&rdquo;可以匹配成功，此时变量&rdquo;:id&rdquo;值为&rdquo;123&rdquo;</p></li>

    <li><p>beego.Router(&ldquo;/user/:username([\\w]+)&ldquo;, &amp;controllers.RController{})</p>

        <p>正则字符串匹配 //例如对于URL&rdquo;/user/astaxie&rdquo;可以匹配成功，此时变量&rdquo;:username&rdquo;值为&rdquo;astaxie&rdquo;</p></li>

    <li><p>beego.Router(&ldquo;/download/*.*&rdquo;, &amp;controllers.RController{})</p>

        <p>*匹配方式 //例如对于URL&rdquo;/download/file/api.xml&rdquo;可以匹配成功，此时变量&rdquo;:path&rdquo;值为&rdquo;file/api&rdquo;， &ldquo;:ext&rdquo;值为&rdquo;xml&rdquo;</p></li>

    <li><p>beego.Router(&ldquo;/download/ceshi/*&ldquo;, &amp;controllers.RController{})</p>

        <p>*全匹配方式 //例如对于URL&rdquo;/download/ceshi/file/api.json&rdquo;可以匹配成功，此时变量&rdquo;:splat&rdquo;值为&rdquo;file/api.json&rdquo;</p></li>

    <li><p>beego.Router(&ldquo;/:id:int&rdquo;, &amp;controllers.RController{})</p>

        <p>int 类型设置方式，匹配 :id为int 类型，框架帮你实现了正则 ([0-9]+)</p></li>

    <li><p>beego.Router(&ldquo;/:hi:string&rdquo;, &amp;controllers.RController{})</p>

        <p>string 类型设置方式，匹配 :hi 为 string 类型。框架帮你实现了正则 ([\w]+)</p></li>

    <li><p>beego.Router(&ldquo;/cms_:id([0-9]+).html&rdquo;, &amp;controllers.CmsController{})</p>

        <p>带有前缀的自定义正则 //匹配 :id 为正则类型。匹配 cms_123.html 这样的 url :id = 123</p></li>
</ul>

<p>可以在 Controller 中通过如下方式获取上面的变量：</p>

<pre><code>this.Ctx.Input.Param(&quot;:id&quot;)
this.Ctx.Input.Param(&quot;:username&quot;)
this.Ctx.Input.Param(&quot;:splat&quot;)
this.Ctx.Input.Param(&quot;:path&quot;)
this.Ctx.Input.Param(&quot;:ext&quot;)
</code></pre>

<h2>自定义方法及 RESTful 规则</h2>

<p>上面列举的是默认的请求方法名（请求的 method 和函数名一致，例如 <code>GET</code> 请求执行 <code>Get</code> 函数，<code>POST</code> 请求执行 <code>Post</code> 函数），如果用户期望自定义函数名，那么可以使用如下方式：</p>

<pre><code>beego.Router(&quot;/&quot;,&amp;IndexController{},&quot;*:Index&quot;)
</code></pre>

<p>使用第三个参数，第三个参数就是用来设置对应 method 到函数名，定义如下</p>

<ul>
    <li><code>*</code>表示任意的 method 都执行该函数<br /></li>
    <li>使用 httpmethod:funcname 格式来展示<br /></li>
    <li>多个不同的格式使用 <code>;</code> 分割<br /></li>
    <li>多个 method 对应同一个 funcname，method 之间通过 <code>,</code> 来分割<br /></li>
</ul>

<p>以下是一个 RESTful 的设计示例：</p>

<pre><code>beego.Router(&quot;/api/list&quot;,&amp;RestController{},&quot;*:ListFood&quot;)
beego.Router(&quot;/api/create&quot;,&amp;RestController{},&quot;post:CreateFood&quot;)
beego.Router(&quot;/api/update&quot;,&amp;RestController{},&quot;put:UpdateFood&quot;)
beego.Router(&quot;/api/delete&quot;,&amp;RestController{},&quot;delete:DeleteFood&quot;)
</code></pre>

<p>以下是多个 HTTP Method 指向同一个函数的示例：</p>

<pre><code>beego.Router(&quot;/api&quot;,&amp;RestController{},&quot;get,post:ApiFunc&quot;)
</code></pre>

<p>以下是不同的 method 对应不同的函数，通过 ; 进行分割的示例：</p>

<pre><code>beego.Router(&quot;/simple&quot;,&amp;SimpleController{},&quot;get:GetFunc;post:PostFunc&quot;)
</code></pre>

<p>可用的 HTTP Method：</p>

<ul>
    <li>*: 包含以下所有的函数<br /></li>
    <li>get: GET 请求<br /></li>
    <li>post: POST 请求<br /></li>
    <li>put: PUT 请求<br /></li>
    <li>delete: DELETE 请求<br /></li>
    <li>patch: PATCH 请求<br /></li>
    <li>options: OPTIONS 请求<br /></li>
    <li>head: HEAD 请求<br /></li>
</ul>

<p>如果同时存在 * 和对应的 HTTP Method，那么优先执行 HTTP Method 的方法，例如同时注册了如下所示的路由：</p>

<pre><code>beego.Router(&quot;/simple&quot;,&amp;SimpleController{},&quot;*:AllFunc;post:PostFunc&quot;)
</code></pre>

<p>那么执行 <code>POST</code> 请求的时候，执行 <code>PostFunc</code> 而不执行 <code>AllFunc</code>。</p>

<blockquote>
    <blockquote>
        <blockquote>
            <p>自定义函数的路由默认不支持 RESTful 的方法，也就是如果你设置了 <code>beego.Router(&quot;/api&quot;,&amp;RestController{},&quot;post:ApiFunc&quot;)</code> 这样的路由，如果请求的方法是 <code>POST</code>，那么不会默认去执行 <code>Post</code> 函数。</p>
        </blockquote>
    </blockquote>
</blockquote>

<h2>自动匹配</h2>

<p>用户首先需要把需要路由的控制器注册到自动路由中：</p>

<pre><code>beego.AutoRouter(&amp;controllers.ObjectController{})
</code></pre>

<p>那么 beego 就会通过反射获取该结构体中所有的实现方法，你就可以通过如下的方式访问到对应的方法中：</p>

<pre><code>/object/login   调用 ObjectController 中的 Login 方法
/object/logout  调用 ObjectController 中的 Logout 方法
</code></pre>

<p>除了前缀两个 <code>/:controller/:method</code> 的匹配之外，剩下的 url beego 会帮你自动化解析为参数，保存在 <code>this.Ctx.Input.Params</code> 当中：</p>

<pre><code>/object/blog/2013/09/12  调用 ObjectController 中的 Blog 方法，参数如下：map[0:2013 1:09 2:12]
</code></pre>

<p>方法名在内部是保存了用户设置的，例如 Login，url 匹配的时候都会转化为小写，所以，<code>/object/LOGIN</code> 这样的 <code>url</code> 也一样可以路由到用户定义的 <code>Login</code> 方法中。</p>

<p>现在已经可以通过自动识别出来下面类似的所有 url，都会把请求分发到 <code>controller</code> 的 <code>simple</code> 方法：</p>

<pre><code>/controller/simple
/controller/simple.html
/controller/simple.json
/controller/simple.xml
</code></pre>

<p>可以通过 <code>this.Ctx.Input.Param(&quot;:ext&quot;)</code> 获取后缀名。</p>

<h2>注解路由</h2>

<p>从 beego 1.3 版本开始支持了注解路由，用户无需在 router 中注册路由，只需要 Include 相应地 controller，然后在 controller 的 method 方法上面写上 router 注释（// @router）就可以了，详细的使用请看下面的例子：</p>

<pre><code>// CMS API
type CMSController struct {
    beego.Controller
}

func (c *CMSController) URLMapping() {
    c.Mapping(&quot;StaticBlock&quot;, c.StaticBlock)
    c.Mapping(&quot;AllBlock&quot;, c.AllBlock)
}


// @router /staticblock/:key [get]
func (this *CMSController) StaticBlock() {

}

// @router /all/:key [get]
func (this *CMSController) AllBlock() {

}
</code></pre>

<p>可以在 <code>router.go</code> 中通过如下方式注册路由：</p>

<pre><code>beego.Include(&amp;CMSController{})
</code></pre>

<p>beego 自动会进行源码分析，注意只会在 dev 模式下进行生成，生成的路由放在 &ldquo;/routers/commentsRouter.go&rdquo; 文件中。</p>

<p>这样上面的路由就支持了如下的路由：</p>

<ul>
    <li>GET /staticblock/:key<br /></li>
    <li>GET /all/:key<br /></li>
</ul>

<p>其实效果和自己通过 Router 函数注册是一样的：</p>

<pre><code>beego.Router(&quot;/staticblock/:key&quot;, &amp;CMSController{}, &quot;get:StaticBlock&quot;)
beego.Router(&quot;/all/:key&quot;, &amp;CMSController{}, &quot;get:AllBlock&quot;)
</code></pre>

<p>同时大家注意到新版本里面增加了 URLMapping 这个函数，这是新增加的函数，用户如果没有进行注册，那么就会通过反射来执行对应的函数，如果注册了就会通过 interface 来进行执行函数，性能上面会提升很多。</p>

<h2>namespace</h2>

<pre><code>//初始化 namespace
ns :=
beego.NewNamespace(&quot;/v1&quot;,
    beego.NSCond(func(ctx *context.Context) bool {
        if ctx.Input.Domain() == &quot;api.beego.me&quot; {
            return true
        }
        return false
    }),
    beego.NSBefore(auth),
    beego.NSGet(&quot;/notallowed&quot;, func(ctx *context.Context) {
        ctx.Output.Body([]byte(&quot;notAllowed&quot;))
    }),
    beego.NSRouter(&quot;/version&quot;, &amp;AdminController{}, &quot;get:ShowAPIVersion&quot;),
    beego.NSRouter(&quot;/changepassword&quot;, &amp;UserController{}),
    beego.NSNamespace(&quot;/shop&quot;,
        beego.NSBefore(sentry),
        beego.NSGet(&quot;/:id&quot;, func(ctx *context.Context) {
            ctx.Output.Body([]byte(&quot;notAllowed&quot;))
        }),
    ),
    beego.NSNamespace(&quot;/cms&quot;,
        beego.NSInclude(
            &amp;controllers.MainController{},
            &amp;controllers.CMSController{},
            &amp;controllers.BlockController{},
        ),
    ),
)
//注册 namespace
beego.AddNamespace(ns)
</code></pre>

<p>上面这个代码支持了如下这样的请求 URL</p>

<ul>
    <li>GET /v1/notallowed<br /></li>
    <li>GET /v1/version<br /></li>
    <li>GET /v1/changepassword<br /></li>
    <li>POST /v1/changepassword<br /></li>
    <li>GET /v1/shop/123<br /></li>
    <li>GET /v1/cms/ 对应 MainController、CMSController、BlockController 中得注解路由<br /></li>
</ul>

<p>而且还支持前置过滤,条件判断,无限嵌套 namespace</p>

<p>namespace 的接口如下:</p>

<ul>
    <li><p>NewNamespace(prefix string, funcs &hellip;interface{})</p>

        <p>初始化 namespace 对象,下面这些函数都是 namespace 对象的方法,但是强烈推荐使用 NS 开头的相应函数注册，因为这样更容易通过 gofmt 工具看的更清楚路由的级别关系</p></li>

    <li><p>NSCond(cond namespaceCond)</p>

        <p>支持满足条件的就执行该 namespace, 不满足就不执行</p></li>

    <li><p>NSBefore(filiterList &hellip;FilterFunc)</p></li>

    <li><p>NSAfter(filiterList &hellip;FilterFunc)</p>

        <p>上面分别对应 beforeRouter 和 FinishRouter 两个过滤器，可以同时注册多个过滤器</p></li>

    <li><p>NSInclude(cList &hellip;ControllerInterface)</p></li>

    <li><p>NSRouter(rootpath string, c ControllerInterface, mappingMethods &hellip;string)</p></li>

    <li><p>NSGet(rootpath string, f FilterFunc)</p></li>

    <li><p>NSPost(rootpath string, f FilterFunc)</p></li>

    <li><p>NSDelete(rootpath string, f FilterFunc)</p></li>

    <li><p>NSPut(rootpath string, f FilterFunc)</p></li>

    <li><p>NSHead(rootpath string, f FilterFunc)</p></li>

    <li><p>NSOptions(rootpath string, f FilterFunc)</p></li>

    <li><p>NSPatch(rootpath string, f FilterFunc)</p></li>

    <li><p>NSAny(rootpath string, f FilterFunc)</p></li>

    <li><p>NSHandler(rootpath string, h http.Handler)</p></li>

    <li><p>NSAutoRouter(c ControllerInterface)</p></li>

    <li><p>NSAutoPrefix(prefix string, c ControllerInterface)</p>

        <p>上面这些都是设置路由的函数,详细的使用和上面 beego 的对应函数是一样的</p></li>

    <li><p>NSNamespace(prefix string, params &hellip;innnerNamespace)</p>

        <p>嵌套其他 namespace</p>

        <pre><code>ns :=
beego.NewNamespace(&quot;/v1&quot;,
    beego.NSNamespace(&quot;/shop&quot;,
        beego.NSGet(&quot;/:id&quot;, func(ctx *context.Context) {
            ctx.Output.Body([]byte(&quot;shopinfo&quot;))
        }),
    ),
    beego.NSNamespace(&quot;/order&quot;,
        beego.NSGet(&quot;/:id&quot;, func(ctx *context.Context) {
            ctx.Output.Body([]byte(&quot;orderinfo&quot;))
        }),
    ),
    beego.NSNamespace(&quot;/crm&quot;,
        beego.NSGet(&quot;/:id&quot;, func(ctx *context.Context) {
            ctx.Output.Body([]byte(&quot;crminfo&quot;))
        }),
    ),
)
</code></pre></li>
</ul>

<p>下面这些函数都是属于 *Namespace 对象的方法：不建议直接使用，当然效果和上面的 NS 开头的函数是一样的，只是上面的方式更优雅，写出来的代码更容易看得懂</p>

<ul>
    <li><p>Cond(cond namespaceCond)</p>

        <p>支持满足条件的就执行该 namespace, 不满足就不执行,例如你可以根据域名来控制 namespace</p></li>

    <li><p>Filter(action string, filter FilterFunc)</p>

        <p>action 表示你需要执行的位置, before 和 after 分别表示执行逻辑之前和执行逻辑之后的 filter</p></li>

    <li><p>Router(rootpath string, c ControllerInterface, mappingMethods &hellip;string)</p></li>

    <li><p>AutoRouter(c ControllerInterface)</p></li>

    <li><p>AutoPrefix(prefix string, c ControllerInterface)</p></li>

    <li><p>Get(rootpath string, f FilterFunc)</p></li>

    <li><p>Post(rootpath string, f FilterFunc)</p></li>

    <li><p>Delete(rootpath string, f FilterFunc)</p></li>

    <li><p>Put(rootpath string, f FilterFunc)</p></li>

    <li><p>Head(rootpath string, f FilterFunc)</p></li>

    <li><p>Options(rootpath string, f FilterFunc)</p></li>

    <li><p>Patch(rootpath string, f FilterFunc)</p></li>

    <li><p>Any(rootpath string, f FilterFunc)</p></li>

    <li><p>Handler(rootpath string, h http.Handler)</p>

        <p>上面这些都是设置路由的函数,详细的使用和上面 beego 的对应函数是一样的</p></li>

    <li><p>Namespace(ns &hellip;*Namespace)</p></li>
</ul>

<p>更多的例子代码：</p>

<pre><code>//APIS
ns :=
    beego.NewNamespace(&quot;/api&quot;,
        //此处正式版时改为验证加密请求
        beego.NSCond(func(ctx *context.Context) bool {
            if ua := ctx.Input.Request.UserAgent(); ua != &quot;&quot; {
                return true
            }
            return false
        }),
        beego.NSNamespace(&quot;/ios&quot;,
            //CRUD Create(创建)、Read(读取)、Update(更新)和Delete(删除)
            beego.NSNamespace(&quot;/create&quot;,
                // /api/ios/create/node/
                beego.NSRouter(&quot;/node&quot;, &amp;apis.CreateNodeHandler{}),
                // /api/ios/create/topic/
                beego.NSRouter(&quot;/topic&quot;, &amp;apis.CreateTopicHandler{}),
            ),
            beego.NSNamespace(&quot;/read&quot;,
                beego.NSRouter(&quot;/node&quot;, &amp;apis.ReadNodeHandler{}),
                beego.NSRouter(&quot;/topic&quot;, &amp;apis.ReadTopicHandler{}),
            ),
            beego.NSNamespace(&quot;/update&quot;,
                beego.NSRouter(&quot;/node&quot;, &amp;apis.UpdateNodeHandler{}),
                beego.NSRouter(&quot;/topic&quot;, &amp;apis.UpdateTopicHandler{}),
            ),
            beego.NSNamespace(&quot;/delete&quot;,
                beego.NSRouter(&quot;/node&quot;, &amp;apis.DeleteNodeHandler{}),
                beego.NSRouter(&quot;/topic&quot;, &amp;apis.DeleteTopicHandler{}),
            )
        ),
    )

beego.AddNamespace(ns)
</code></pre>